/*
 *  Copyright (C) 2004 Cidero, Inc.
 *
 *  Permission is hereby granted to any person obtaining a copy of 
 *  this software to use, copy, modify, merge, publish, and distribute
 *  the software for any non-commercial purpose, subject to the
 *  following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY IN CONNECTION WITH THE SOFTWARE.
 * 
 *  File: $RCSfile: SlimRenderingControl.java,v $
 *
 */

package com.cidero.bridge.slim;

import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;

import org.cybergarage.upnp.Action;
import org.cybergarage.upnp.StateVariable;
import org.cybergarage.upnp.device.InvalidDescriptionException;

import com.cidero.upnp.RenderingControl;

/**
 * Primiq UPnP MediaRenderer Bridge RenderingControl Service class. 
 *
 */
public class SlimRenderingControl extends RenderingControl
                                     implements Observer
{
  private static Logger logger = 
    Logger.getLogger("com.cidero.bridge.slim");

  SlimMediaRenderer mediaRenderer;
  
  /**
   * Constructor
   *
   */
  public SlimRenderingControl( SlimMediaRenderer mediaRenderer )
    throws InvalidDescriptionException
  {
    super( mediaRenderer );

    logger.fine("Entered SlimRenderingControl constructor");

    this.mediaRenderer = mediaRenderer;

    mediaRenderer.getStateModel().addObserver(this);

    logger.fine("Leaving SlimRenderingControl constructor");
  }

  /**
   *  Initialize state variables. Note that required state variables 
   *  have been given 'reasonable' default values in the base class
   *  version of this routine 
   */
  public void initializeStateVariables()
  {
    super.initializeStateVariables();
    
    setStateVariable("Volume", "50" );
    setStateVariable("Mute", "0" );
  }
  
  /**
   * Set volume on Slim. On success, update UPnP state variable
   *
   * Note that GetVolume action in base class reads/returns the state 
   * variable, so no custom version of actionGetVolume() is needed
   *
   */
  public boolean actionSetVolume( Action action )
  {
    // Volume is 0-100
    String volume = action.getArgumentValue("DesiredVolume");  
    logger.fine("SetVolume: Entered - DesiredVolume = " + volume );

    if( mediaRenderer.send( "SET AUDIO_VOLUME " + volume + "\n" ) == null )
      return false;


    // Update UPnP state variable & trigger LastChange event for benefit
    // of all control points. Clear Mute state if it was set
    updateStateVariable( "Volume", volume );
    if( ! volume.equals("0") )
      updateStateVariable( "Mute", "0" );

    return true;
  }

  /**
   * Set mute on Slim. On success, update UPnP state variable
   *
   * Mute is not supported by the Slim media agent API, so it is 
   * emulated using the volume setting
   */
  public boolean actionSetMute( Action action )
  {
    logger.fine("SetMute: Entered " );

    String response;
    String mute = action.getArgumentValue( "DesiredMute" );

    if( mute.equals("1") )
    {
      //
      // Set volume to 0 the Slim, and set the mute state variable
      // (not the volume state variable - it needs to stay the same
      // to implement the 'unmute'!)
      //
      if( mediaRenderer.send( "SET AUDIO_VOLUME 0\n" ) == null )
        return false;

      updateStateVariable( "Mute", "1" );
    }
    else
    {
      // Unmute by resetting the volume on the Slim to match the UPnP 
      // state variable
      StateVariable volumeVar = getStateVariable( "Volume" );
      if( mediaRenderer.send( "SET AUDIO_VOLUME " + 
                              volumeVar.getValue() + "\n" ) == null )
        return false;
      
      updateStateVariable( "Mute", "0" );
    }
    
    return true;
  }

  /**
   *  RenderingControl is an observer of Slim RendererStatusModel so 
   *  that it can pick up state changes made using the Slim remote.
   *  If the RendererStatusModel info differs from the UPnP state variables,
   *  update the state variables and fire off a LastChange event
   */
  public void update( Observable model, Object arg )
  {
    SlimStateModel stateModel = (SlimStateModel)model;
    
    // Update the UPnP state variables to match the state model
    // (Automatically fires LastChange events)
    logger.fine("Updating UPnP state from model - volume: " + 
                stateModel.getVolumeString() + " mute: " + 
                stateModel.getMuteString() );

    updateStateVariable( "Volume", stateModel.getVolumeString() );
    updateStateVariable( "Mute", stateModel.getMuteString() );
  }
  
}

